iT邦幫忙

2023 iThome 鐵人賽

DAY 1
0
Modern Web

一些讓你看來很強的全端- trcp 伴讀系列 第 23

Day-023. 一些讓你看來很強的全端 TRPC 伴讀 -trpc test

  • 分享至 

  • xImage
  •  

今天來寫一下 api union test 這是會用 vitestprisma 做一些 mock datatest case 那我們廢話不多說馬上來看怎麼做的吧~

環境設定

install 以下的 package

> npm i  @vitejs/plugin-react
> npm i  vite-tsconfig-paths   
> npm i  -D vitest

package.json 中加 scripts

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "test": "vitest"
},

在專案新增 vitest.config.js

// vitest.config.js
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
  test: {
    globals: true,
  },
  plugins: [tsconfigPaths(), react()],
});

globals: true

大家如果寫過 test 就知道每次要用 describe 或是 it 都要 import

import { beforeEach, describe, expect, it } from "vitest";

describe('trpc api', () => {
    //..

設定成 globals: true 則告訴 test 只要看到 describe 就是用 vitestlib


describe('trpc api', () => {
    //..

mock data

哪因為我們這次的 test case 打算用 mock data 的形式,我們不希望因為跑 test case 直接就更改到 db 資料,所以我們需要透過 mock data 寫法。

vitest 有提供 mock 套件,然後請讀者 install 下面得 lib

npm i -D vitest-mock-extended

接著我們把 prisma mock 起來,prismaMock 就是取代原本的 PrismaClientinstance

例外不要忘記我們要mockprisma 位置這邊我們是放在 ../db 中,vi.restoreAllMocks() 以及 mock 之後要記得 restore,確保比次的 test case 都是獨立的 data

import { PrismaClient } from '@prisma/client'
import { beforeEach, vi } from 'vitest'
import { mockDeep } from 'vitest-mock-extended'
vi.mock('../db')
beforeEach(() => {
  vi.restoreAllMocks()
})

export const prismaMock = mockDeep<PrismaClient>()

test case

原本我們不是透過 createTRPCContext create context 嗎,因為 test 關西我們要額外把 session 拿出來。

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
  const { req, res } = opts
  const session = await getServerAuthSession({ req, res });
  return {
    session: opts.session,
    prisma,
  };
};

這樣我們就可以透過 createInnerTRPCContext function create session return 了。

type CreateContextOptions = {
  session: Session | null;
};
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
  return {
    session: opts.session,
    prisma,
  };
};

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
  const { req, res } = opts
  const session = await getServerAuthSession({ req, res });
  return createInnerTRPCContext({ session });
};

之後新增 ~src/server/api/_test_/index.test.ts ,然後把會用到的套件跟 mock data 先透過 faker 幫我們隨機生成。

mockData : user 資料。
mockUserSession : session 內容。

import { AppRouter, appRouter } from "../root";
import { inferProcedureInput } from "@trpc/server";
import { createInnerTRPCContext } from "../trpc";
import { prismaMock } from "@/server/__mocks__/prisma";
import { faker } from '@faker-js/faker'

describe('trpc api', () => {

  let mockData =
  {
    "id": faker.number.int(),
    "title": faker.lorem.slug(),
    "content": faker.lorem.paragraph(2),
    "published": faker.datatype.boolean(0.5),
    "createdAt": faker.date.anytime(),
    "updatedAt": faker.date.anytime(),
    "userId": faker.string.uuid(),
  }
  let mockUserSession = {
    user: {
      id: faker.string.uuid()
    },
    expires: faker.date.anytime().toString()
  }
  let ctx = createInnerTRPCContext({
    session: mockUserSession
  })

開始之前先簡單測試一下 greeting api 會不會 pass

describe('trpc api', () => {
    
//..
    it('greeting input', async () => {
        type GreetInput = inferProcedureInput<AppRouter['greeting']>
        const input: GreetInput = {
          name: 'Danny'
        }
        const results = await caller.greeting(input)
        expect(results).toStrictEqual('hello Danny')
    })

成功~~

https://ithelp.ithome.com.tw/upload/images/20231007/2014567744shlZdi41.png

caller

trpc 有一個 Caller 用法讓你可以在 server apitestreturn datacaller 用法就是呼叫一個 function 當作 call api ,這也是 RPC 架構的精髓~透過 caller 就可以把 api 邏輯包成一個 function~

import { AppRouter, appRouter } from "../root";
import { prismaMock } from "@/server/__mocks__/prisma";

describe('trpc api', () => {
    //..
    
 const caller = appRouter.createCaller({ session: ctx.session, prisma: prismaMock })

getPosts

因為我們的 caller 有把 mock session 資料放進去,所以以下的 results 會試 authorized 的狀態。

透過 prismaMock 實作 mockResolvedValuecaller.posts.getPosts 的結果會是 mockDatas 資料,接著我們速速 demo 其他的 api ~

describe('trpc api', () => {
    
//..
  it('getPosts should return equal mockData', async () => {
        const mockDatas = [mockData]
        prismaMock.post.findMany.mockResolvedValue(mockDatas)
        const results = await caller.posts.getPosts()
        expect(results).toHaveLength(mockDatas.length)
        expect(results).toStrictEqual(mockDatas)
  })

create post

it('success create Post', async () => {
    prismaMock.post.create.mockResolvedValue(mockData)
    const result = await caller.posts.addPost({
      title: mockData.title,
      content: mockData.content
    })
    expect(result).toStrictEqual({
      message: 'success create post',
      id: mockData.id
    })
})

toggle post

it('success toggle post published type', async () => {
    prismaMock.post.update.mockResolvedValue(mockData)
    const result = await caller.posts.togglePostPublish({ id: mockData.id, published: !mockData.published })
    expect(result).toStrictEqual({
      message: 'success update post',
      id: mockData.id
    })
  })

delete post

it('success delete post ', async () => {
    prismaMock.post.delete.mockResolvedValue(mockData)
    const result = await caller.posts.deletePost({ id: mockData.id })
    expect(result).toStrictEqual({
      message: 'success delete post',
      id: mockData.id
    })
})

infinite posts

 it('success get infinite Posts ', async () => {
    prismaMock.post.findMany.mockResolvedValue([mockData])
    const result = await caller.posts.infinitePosts({ limit: 10 })
    console.log(result)
    expect(result).toStrictEqual({
      posts: [mockData],
      nextCursor: undefined
    })
 })

這樣我們就把 CRUDtest 都完成拉~是不是很簡單呢~ 最後我們 run npm run test 看一下結果~

All pass ~

https://ithelp.ithome.com.tw/upload/images/20231007/20145677Co5SHFJPlC.png
這樣各位小夥伴應該就會寫 apimock test 了 ~

相關連結

https://tawaldevuniverse.hashnode.dev/some-tips-when-using-t3-stack-unit-testing-with-trpc-procedures-environment-setup#heading-final-setup
https://www.prisma.io/blog/testing-series-1-8eRB5p0Y8o


上一篇
Day-022. 一些讓你看來很強的全端 TRPC 伴讀 -withAuth
下一篇
Day-024. 一些讓你看來很強的全端 TRPC 伴讀 -TRPC with App router
系列文
一些讓你看來很強的全端- trcp 伴讀30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言